#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hash_table.h"



/**************************macro***********************************/
#define g_error(format,...) printf("[%s,%s,%d]:\033[1;33m" format "\033[0m\n",__FILE__,__FUNCTION__,__LINE__, ##__VA_ARGS__) 
#define g_ok(format,...) printf("[%s,%s,%d]:\033[2;32m" format "\033[0m\n",__FILE__,__FUNCTION__,__LINE__, ##__VA_ARGS__) 

#define HASH_TABLE_PARA_CHECK(para) \
if(NULL == para)\
{\
	g_error("parameter is invalid.\n");\
	return -1;\
}


static short _crc16_cksum (const char *pptr, int checksum_offset, int length)
{

    int x;
    int y;
    int mul;
    int c0;
    int c1;
    short checksum;
    int index;

    checksum = 0;

    c0 = 0;
    c1 = 0;

    for (index = 0; index < length; index++) {
        /*
         * Ignore the contents of the checksum field.
         */
        if (index == checksum_offset ||
            index == checksum_offset+1) {
            c1 += c0;
            pptr++;
        } else {
            c0 = c0 + *(pptr++);
            c1 += c0;
        } 
    }

    c0 = c0 % 255;
    c1 = c1 % 255;

    mul = (length - checksum_offset)*(c0);
  
    x = mul - c0 - c1;
    y = c1 - mul - 1;

    if ( y >= 0 ) y++;
    if ( x < 0 ) x--;

    x %= 255;
    y %= 255;


    if (x == 0) x = 255;
    if (y == 0) y = 255;

    y &= 0x00FF;
    checksum = ((x << 8) | y);
  
    return checksum;
}



static inline int hash_table_get_size(hx_hash_table *hash_table)
{
	int len = 0;
	
	if (NULL == hash_table)
		return len;
	
	len =  sizeof(hash_table->hash_node)/sizeof(hx_hash_node*);

	return len;
}

static inline short hash_table_crc16(char *point)
{
	short csm = 0;
	char buff[16] = {0};
	
	if (NULL == point)
		return 0;
		
	csm = _crc16_cksum(point,0,strlen(point));	
		
		
	return csm;
}

static int hash_table_print(hx_hash_table *hash_table)
{
	int	size = hash_table_get_size(hash_table);
	int loop;
	hx_hash_node *node;

	for (loop = 0; loop < size; loop++)
	{
		
		node = hash_table->hash_node[loop];
		if (!node)
			continue;
		g_ok("index=%d",loop);
		
		while (node)
		{
			g_ok("key=%s",node->key);
			g_ok("value=%s",node->value);
			node = node->pnext;
		}

		g_ok("\n");
	}

	return 0;
}


hx_hash_node *hash_table_find(hx_hash_table *hash_table,char *key)
{
	int	size = hash_table_get_size(hash_table);
	int idx;
	hx_hash_node *node = NULL;

	if (NULL == hash_table
		|| NULL == key)
		return NULL;
	
		
	idx = hash_table_crc16(key)%size;
	node = hash_table->hash_node[idx];

	if (NULL == node)
		return NULL;

	while (node)
	{
		if (0 == strcmp(key,node->key))
			return node;

		node = node->pnext;
	}

	return NULL;
}
	

int hash_table_insert(hx_hash_table *hash_table,char *key,char *value,int value_len)
{
	int	size = hash_table_get_size(hash_table);
	int idx;
	hx_hash_node *node = NULL;

	if (0 == value_len)
	{
		g_error("parameter is invalid");
		return -1;
	}

	HASH_TABLE_PARA_CHECK(hash_table)
	HASH_TABLE_PARA_CHECK(key)
	HASH_TABLE_PARA_CHECK(value)

	idx = hash_table_crc16(key)%size;

	
	if (NULL == hash_table->hash_node[idx])//node is not exist
	{
		node = (hx_hash_node*)malloc(sizeof(hx_hash_node));
		HASH_TABLE_PARA_CHECK(node)
		memset((char*)node,0,sizeof(hx_hash_node));
		snprintf(node->key,sizeof(node->key),"%s",key);
		node->value = (char*)malloc(value_len);
		if (NULL == node->value)
		{
			free(node);
			node = NULL;
			return -1;
		}

		memset(node->value,0,value_len);
		memcpy(node->value,value,value_len);
		
		hash_table->hash_node[idx]=node;

		return 0;
	}

	node = hash_table_find(hash_table,key);//update the node
	if (node)
	{
/*
		free(node->value);
		node->value = (char*)malloc(value_len);
		HASH_TABLE_PARA_CHECK(node->value)
		memset(node->value,0,value_len);
		memcpy(node->value,value,value_len);
*/
		g_error("the key %s is exist",key);
			
		return -1;
	}

	/*add new node*/
	node = hash_table->hash_node[idx];
	while (NULL != node->pnext)
		node = node->pnext;

	
	node->pnext = (hx_hash_node*)malloc(sizeof(hx_hash_node));
	HASH_TABLE_PARA_CHECK(node->pnext)
	memset(node->pnext,0,sizeof(hx_hash_node));
	node->pnext->value	= (char*)malloc(value_len);
	if (NULL == node->pnext->value)
	{
		free(node->pnext);
		node->pnext = NULL;
		return -1;
	}
	memset(node->pnext->value,0,value_len);
	
	strcpy(node->pnext->key,key);
	memcpy(node->pnext->value,value,value_len);
	
	return 0;
}


int hash_table_del(hx_hash_table *hash_table,char *key)
{
	int i;
	int size;
	int idx;
	hx_hash_node *node = hash_table_find(hash_table,key);

	if (NULL == node)
	{
		g_error("the key node is not exist");
		return -1;
	}

	size = hash_table_get_size(hash_table);
	idx = hash_table_crc16(key)%size;

	node = hash_table->hash_node[idx];

	if (0 == strcmp(key,node->key))
	{
		hash_table->hash_node[idx] = node->pnext;
		free(node);
		return 0;
	}

	while (node->pnext)
	{
		if (0 == strcmp(key,node->pnext->key))
		{
			node->pnext = node->pnext->pnext;
			free(node->pnext);
			return 0;
		}

	}


	return -1;
}


hx_hash_table * hash_table_create()
{
	hx_hash_table *hash_table = NULL;
	
	hash_table = (hx_hash_table*)malloc(sizeof(hx_hash_table));

	if (NULL == hash_table)
	{
		return NULL;
	}
	
	memset(hash_table,0,sizeof(hx_hash_table));
	
	return hash_table;
}



int hash_table_destroy(hx_hash_table *hash_table)
{
	int i;
	int	size = hash_table_get_size(hash_table);
	hx_hash_node *node;
	
	HASH_TABLE_PARA_CHECK(hash_table)

	for (i = 0; i < size; i++)
	{
		if ((node=hash_table->hash_node[i]))
		{
			while(node->pnext)
			{
				node->pnext = node->pnext->pnext;
				free(node->pnext);
			}

			free(node);
		}
	}
	
	return 0;	
}


int main(int argc, char* argv[])
{
	char key1[32]="cbb";
	char value1[32]="123456";
	char key2[32]="xxx";
	char value2[32]="xxxxxxxxxxxxxl";
	hx_hash_node *node;

	hx_hash_table *hash_table = hash_table_create();
	hash_table_insert(hash_table, key1, value1, strlen(value1));
	hash_table_insert(hash_table, key2, value2, strlen(value2));
	

	hash_table_print(hash_table);


	node = hash_table_find(hash_table, "cbb");
	if (node)
	{
		g_ok("value is %s",node->value);
	}
	else
	{
		g_ok("NULL.............");
	}
	

	

	hash_table_destroy(hash_table);

	return 0;
}


